// bdlb_nullablevalue.h                                               -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BDLB_NULLABLEVALUE
#define INCLUDED_BDLB_NULLABLEVALUE

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a template for nullable (in-place) objects.
//
//@CLASSES:
//  bdlb::NullableValue: template for nullable (in-place) objects
//
//@SEE_ALSO: bdlb_nullableallocatedvalue
//
//@DESCRIPTION: This component provides a template class,
// 'bdlb::NullableValue<TYPE>', that can be used to augment an arbitrary
// value-semantic 'TYPE', such as 'int' or 'bsl::string', so that it also
// supports the notion of a "null" value.  That is, the set of values
// representable by the template parameter 'TYPE' is extended to include null.
// If the underlying 'TYPE' is fully value-semantic, then so will the augmented
// type 'bdlb::NullableValue<TYPE>'.  Two homogeneous nullable objects have the
// same value if their underlying (non-null) 'TYPE' values are the same, or
// both are null.
//
// Note that the object of template parameter 'TYPE' that is managed by a
// 'bdlb::NullableValue<TYPE>' object is created *in*-*place*.  Consequently,
// the template parameter 'TYPE' must be a complete type when the class is
// instantiated.  In contrast, 'bdlb::NullableAllocatedValue<TYPE>' (see
// 'bdlb_nullableallocatedvalue') does not require that 'TYPE' be complete when
// that class is instantiated, with the trade-off that the managed 'TYPE'
// object is always allocated out-of-place in that case.
//
// In addition to the standard homogeneous, value-semantic, operations such as
// copy construction, copy assignment, equality comparison, and BDEX streaming,
// 'bdlb::NullableValue' also supports conversion between augmented types for
// which the underlying types are convertible, i.e., for heterogeneous copy
// construction, copy assignment, and equality comparison (e.g., between 'int'
// and 'double'); attempts at conversion between incompatible types, such as
// 'int' and 'bsl::string', will fail to compile.  Note that these operational
// semantics are similar to those found in 'bsl::shared_ptr'.
//
// Furthermore, a move constructor (taking an optional allocator) and a
// move-assignment operator are also provided.  Note that move semantics are
// emulated with C++03 compilers.
//
///Usage
///-----
// The following snippets of code illustrate use of this component:
//
// First, create a nullable 'int' object:
//..
//  bdlb::NullableValue<int> nullableInt;
//  assert( nullableInt.isNull());
//..
// Next, give the 'int' object the value 123 (making it non-null):
//..
//  nullableInt.makeValue(123);
//  assert(!nullableInt.isNull());
//  assert(123 == nullableInt.value());
//..
// Finally, reset the object to its default constructed state (i.e., null):
//..
//  nullableInt.reset();
//  assert( nullableInt.isNull());
//..

#include <bdlscm_version.h>

#include <bdlb_nullopt.h>
#include <bdlb_printmethods.h>

#include <bslalg_swaputil.h>

#include <bslma_constructionutil.h>
#include <bslma_stdallocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_allocatorargt.h>
#include <bslmf_conditional.h>
#include <bslmf_enableif.h>
#include <bslmf_isbitwisemoveable.h>
#include <bslmf_isconvertible.h>
#include <bslmf_istriviallycopyable.h>
#include <bslmf_movableref.h>
#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_assert.h>
#include <bsls_compilerfeatures.h>
#include <bsls_deprecate.h>
#include <bsls_keyword.h>
#include <bsls_objectbuffer.h>
#include <bsls_review.h>

#include <bslstl_optional.h>

#include <bslx_instreamfunctions.h>
#include <bslx_outstreamfunctions.h>
#include <bslx_versionfunctions.h>

#include <bsl_algorithm.h>
#include <bsl_iosfwd.h>
#include <bsl_new.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslalg_typetraits.h>
#include <bslmf_if.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

#if BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
// Include version that can be compiled with C++03
// Generated on Fri Jan 22 15:32:43 2021
// Command line: sim_cpp11_features.pl bdlb_nullablevalue.h
# define COMPILING_BDLB_NULLABLEVALUE_H
# include <bdlb_nullablevalue_cpp03.h>
# undef COMPILING_BDLB_NULLABLEVALUE_H
#else

namespace BloombergLP {
namespace bdlb {

template <class TYPE>
class NullableValue_WithAllocator;

template <class TYPE>
class NullableValue_WithoutAllocator;

                      // =========================
                      // class NullableValue<TYPE>
                      // =========================

template <class TYPE>
class NullableValue : public bsl::optional<TYPE> {
    // This template class extends the set of values of its value-semantic
    // 'TYPE' parameter to include the notion of a "null" value.  If 'TYPE' is
    // fully value-semantic, then the augmented type 'Nullable<TYPE>' will be
    // as well.  In addition to supporting all homogeneous value-semantic
    // operations, conversions between comparable underlying value types is
    // also supported.  Two nullable objects with different underlying types
    // compare equal if their underlying types are comparable and either (1)
    // both objects are null or (2) the non-null values compare equal.  A null
    // nullable object is considered ordered before any non-null nullable
    // object.  Attempts to copy construct, copy assign, or compare
    // incompatible values types will fail to compile.  The 'NullableValue'
    // template cannot be instantiated on an incomplete type, a type that
    // overloads unary 'operator&', or 'bdlb::NullOptType'.

    // PRIVATE TYPES

    typedef bslmf::MovableRefUtil MoveUtil;

    struct EnableType {
    };

    struct NoAlloc {
        // This trivial tag type is used as a dummy when 'NullableValue' wraps
        // a non-allocator-aware type.
    };

    typedef typename bsl::conditional<bslma::UsesBslmaAllocator<TYPE>::value,
                                      bsl::allocator<char>,
                                      NoAlloc>::type AllocType;
        // Type alias to the allocator type used by this 'NullableValue'.  Note
        // that we can't refer to 'optional::allocator_type' because the
        // conditional needs the type to exist even for non allocator aware
        // types.

    // FRIENDS
    template <class ANY_TYPE>
    friend class NullableValue;

  public:
    // TYPES
    typedef TYPE ValueType;
        // 'ValueType' is an alias for the underlying 'TYPE' upon which this
        // template class is instantiated, and represents the type of the
        // managed object.

    typedef AllocType allocator_type;
        // The type of allocator used by this object.  If 'TYPE' is not
        // allocator aware, 'allocator_type' is a private non-allocator type
        // that effectively removes the allocator-specific constructors from
        // consideration during overload resolution.

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION_IF(NullableValue,
                                      bslma::UsesBslmaAllocator,
                                      bslma::UsesBslmaAllocator<TYPE>::value);
    BSLMF_NESTED_TRAIT_DECLARATION_IF(NullableValue,
                                      bsl::is_trivially_copyable,
                                      bsl::is_trivially_copyable<TYPE>::value);
    BSLMF_NESTED_TRAIT_DECLARATION_IF(NullableValue,
                                      bslmf::IsBitwiseMoveable,
                                      bslmf::IsBitwiseMoveable<TYPE>::value);
    BSLMF_NESTED_TRAIT_DECLARATION(NullableValue, bdlb::HasPrintMethod);
        // 'UsesBslmaAllocator', 'IsBitwiseCopyable', and 'IsBitwiseMoveable'
        // are true for 'NullableValue' only if the corresponding trait is true
        // for 'TYPE'.  'HasPrintMethod' is always true for 'NullableValue'.

    // CREATORS
    NullableValue() BSLS_KEYWORD_NOEXCEPT;
        // Create a nullable object having the null value.  If 'TYPE' takes an
        // optional allocator at construction, use the currently installed
        // default allocator to supply memory.

    explicit NullableValue(const allocator_type& allocator)
                                                         BSLS_KEYWORD_NOEXCEPT;
        // Create a nullable object that has the null value and that uses the
        // specified 'allocator' (e.g., the address of a 'bslma::Allocator'
        // object) to supply memory.  Note that this constructor will not
        // participate in overload resolution unless 'TYPE' is allocator aware.

    NullableValue(const NullableValue& original);
        // Create a nullable object having the value of the specified
        // 'original' object.  If 'TYPE' takes an optional allocator at
        // construction, use the currently installed default allocator to
        // supply memory.

    NullableValue(bslmf::MovableRef<NullableValue> original);
        // Create a nullable object having the same value as the specified
        // 'original' object by moving the contents of 'original' to the
        // newly-created object.  If 'TYPE' takes an optional allocator at
        // construction, the allocator associated with 'original' is propagated
        // for use in the newly-created object.  'original' is left in a valid
        // but unspecified state.

    NullableValue(const NullableValue&  original,
                  const allocator_type& allocator);
        // Create a nullable object that has the value of the specified
        // 'original' object and uses the specified 'allocator' (e.g., the
        // address of a 'bslma::Allocator' object) to supply memory.  Note that
        // this constructor will not participate in overload resolution unless
        // 'TYPE' is allocator aware.

    NullableValue(bslmf::MovableRef<NullableValue>  original,
                  const allocator_type&             allocator);
        // Create a nullable object having the same value as the specified
        // 'original' object but using the specified 'allocator' (e.g., the
        // address of a 'bslma::Allocator' object) to supply memory.  The
        // contents of 'original' are moved to the newly-created object using
        // the extended move constructor for 'TYPE'.  'original' is left in a
        // valid but unspecified state.  Note that this constructor will not
        // participate in overload resolution unless 'TYPE' is allocator aware.

    template <class BDE_OTHER_TYPE>
    NullableValue(BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value,
                  typename bsl::enable_if<
                      bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
                      !bsl::is_convertible<BDE_OTHER_TYPE,
                                           allocator_type>::value,
                      EnableType>::type = EnableType());            // IMPLICIT
        // Create a nullable object having the specified 'value' (of
        // 'BDE_OTHER_TYPE') converted to 'TYPE'.  If 'TYPE' takes an optional
        // allocator at construction, use the currently installed default
        // allocator to supply memory.  Note that this constructor will not
        // participate in overload resolution unless 'BDE_OTHER_TYPE' is
        // convertible to 'TYPE' and is not convertible to 'allocator_type'.

    template <class BDE_OTHER_TYPE>
    NullableValue(
             BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value,
             const allocator_type&                             allocator,
             typename bsl::enable_if<
                 bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value,
                 EnableType>::type = EnableType());
        // Create a nullable object that has the specified 'value' (of
        // 'BDE_OTHER_TYPE') converted to 'TYPE' and that uses the specified
        // 'allocator' (e.g., the address of a 'bslma::Allocator' object) to
        // supply memory.  Note that this constructor will not participate in
        // overload resolution unless 'TYPE' is allocator aware and
        // 'BDE_OTHER_TYPE' is convertible to 'TYPE'.

    template <class BDE_OTHER_TYPE>
    explicit NullableValue(const NullableValue<BDE_OTHER_TYPE>& original);
        // Create a nullable object having the null value if the specified
        // 'original' object is null, and the value of 'original.value()' (of
        // 'BDE_OTHER_TYPE') converted to 'TYPE' otherwise.  If 'TYPE' takes an
        // optional allocator at construction, use the currently installed
        // default allocator to supply memory.  Note that this method will fail
        // to compile if 'TYPE and 'BDE_OTHER_TYPE' are not compatible.

    template <class BDE_OTHER_TYPE>
    NullableValue(const NullableValue<BDE_OTHER_TYPE>& original,
                  const allocator_type&                allocator);
        // Create a nullable object that has the null value if the specified
        // 'original' object is null, and the value of 'original.value()' (of
        // 'BDE_OTHER_TYPE') converted to 'TYPE' otherwise.  Use the specified
        // 'allocator' (e.g., the address of a 'bslma::Allocator' object) to
        // supply memory.  Note that this constructor will not partcipate in
        // overload resolution unless 'TYPE' is allocator aware.  Also note
        // that compilation will fail if this function is called with a
        // 'BDE_OTHER_TYPE' that is not convertible to 'TYPE'.

    NullableValue(const NullOptType&) BSLS_KEYWORD_NOEXCEPT;        // IMPLICIT
        // Create a nullable object having the null value.  If 'TYPE' takes an
        // optional allocator at construction, use the currently installed
        // default allocator to supply memory for subsequent values assigned to
        // this object.

    NullableValue(const NullOptType&    ,
                  const allocator_type& allocator) BSLS_KEYWORD_NOEXCEPT;
        // Create a nullable object that has the null value; use the specified
        // 'allocator' (e.g., the address of a 'bslma::Allocator' object) to
        // supply memory for subsequent values assigned to this object.  Note
        // that this constructor will not participate in overload resolution
        // unless 'TYPE' is allocator aware.

    // ~NullableValue();
        // Destroy this object.  Note that this destructor is generated by the
        // compiler.

    // MANIPULATORS
    NullableValue<TYPE>& operator=(const NullableValue& rhs);
        // Assign to this object the value of the specified 'rhs', and return a
        // reference providing modifiable access to this object.

    NullableValue<TYPE>& operator=(bslmf::MovableRef<NullableValue> rhs);
        // Assign to this object the value of the specified 'rhs', and return a
        // reference providing modifiable access to this object.  The contents
        // of 'rhs' are either move-inserted into or move-assigned to this
        // object.  'rhs' is left in a valid but unspecified state.

    template <class BDE_OTHER_TYPE>
    NullableValue<TYPE>& operator=(const NullableValue<BDE_OTHER_TYPE>& rhs);
        // Assign to this object the null value if the specified 'rhs' object
        // is null, and the value of 'rhs.value()' (of 'BDE_OTHER_TYPE')
        // converted to 'TYPE' otherwise.  Return a reference providing
        // modifiable access to this object.  Note that this method will fail
        // to compile if 'TYPE and 'BDE_OTHER_TYPE' are not compatible.

    NullableValue<TYPE>& operator=(const TYPE& rhs);
        // Assign to this object the value of the specified 'rhs', and return a
        // reference providing modifiable access to this object.

    NullableValue<TYPE>& operator=(bslmf::MovableRef<TYPE> rhs);
        // Assign to this object the value of the specified 'rhs', and return a
        // reference providing modifiable access to this object.  The contents
        // of 'rhs' are either move-inserted into or move-assigned to this
        // object.  'rhs' is left in a valid but unspecified state.

    template <class BDE_OTHER_TYPE>
    NullableValue<TYPE>& operator=(const BDE_OTHER_TYPE& rhs);
        // Assign to this object the value of the specified 'rhs' object (of
        // 'BDE_OTHER_TYPE') converted to 'TYPE', and return a reference
        // providing modifiable access to this object.  Note that this method
        // will fail to compile if 'TYPE and 'BDE_OTHER_TYPE' are not
        // compatible.

    NullableValue<TYPE>& operator=(const NullOptType&) BSLS_KEYWORD_NOEXCEPT;
        // Reset this object to the default constructed state (i.e., to have
        // the null value), and return a reference providing modifiable access
        // to this object.

    template <class STREAM>
    STREAM& bdexStreamIn(STREAM& stream, int version);
        // Assign to this object the value read from the specified input
        // 'stream' using the specified 'version' format, and return a
        // reference to 'stream'.  If 'stream' is initially invalid, this
        // operation has no effect.  If 'version' is not supported, this object
        // is unaltered and 'stream' is invalidated, but otherwise unmodified.
        // If 'version' is supported but 'stream' becomes invalid during this
        // operation, this object has an undefined, but valid, state.  Note
        // that no version is read from 'stream'.  See the 'bslx' package-level
        // documentation for more information on BDEX streaming of
        // value-semantic types and containers.

    template <class BDE_OTHER_TYPE>
    TYPE& makeValue(BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value);
        // Assign to this object the specified 'value' (of 'BDE_OTHER_TYPE')
        // converted to 'TYPE', and return a reference providing modifiable
        // access to the underlying 'TYPE' object.  Note that this method will
        // fail to compile if 'TYPE and 'BDE_OTHER_TYPE' are not compatible.

    TYPE& makeValue();
        // Assign to this object the default value for 'TYPE', and return a
        // reference providing modifiable access to the underlying 'TYPE'
        // object.

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=5
    template <class... ARGS>
    TYPE& makeValueInplace(ARGS&&... args);
        // Assign to this nullable object the value of the (template parameter)
        // 'TYPE' created in place using the specified 'args'.  Return a
        // reference providing modifiable access to the created (value) object.
        // The object is also accessible via the 'value' method.  If this
        // nullable object already contains an object ('false == isNull()'),
        // that object is destroyed before the new object is created.  If
        // 'TYPE' has the trait 'bslma::UsesBslmaAllocator' ('TYPE' is
        // allocator-enabled) the allocator specified at the construction of
        // this nullable object is used to supply memory to the value object.
        // Attempts to explicitly specify via 'args' another allocator to
        // supply memory to the created (value) object are disallowed by the
        // compiler.  Note that if the constructor of 'TYPE' throws an
        // exception this object is left in the null state.
#endif

    TYPE& value();
        // Return a reference providing modifiable access to the underlying
        // 'TYPE' object.  The behavior is undefined unless this object is
        // non-null.

    // ACCESSORS
    const TYPE *addressOr(const TYPE *address) const;
        // Return an address providing non-modifiable access to the underlying
        // object of a (template parameter) 'TYPE' if this object is non-null,
        // and the specified 'address' otherwise.

    template <class STREAM>
    STREAM& bdexStreamOut(STREAM& stream, int version) const;
        // Write the value of this object, using the specified 'version'
        // format, to the specified output 'stream', and return a reference to
        // 'stream'.  If 'stream' is initially invalid, this operation has no
        // effect.  If 'version' is not supported, 'stream' is invalidated, but
        // otherwise unmodified.  Note that 'version' is not written to
        // 'stream'.  See the 'bslx' package-level documentation for more
        // information on BDEX streaming of value-semantic types and
        // containers.

    bool isNull() const BSLS_KEYWORD_NOEXCEPT;
        // Return 'true' if this object is null, and 'false' otherwise.

    int maxSupportedBdexVersion(int versionSelector) const;
        // Return the maximum valid BDEX format version, as indicated by the
        // specified 'versionSelector', to be passed to the 'bdexStreamOut'
        // method.  Note that it is highly recommended that 'versionSelector'
        // be formatted as "YYYYMMDD", a date representation.  Also note that
        // 'versionSelector' should be a *compile*-time-chosen value that
        // selects a format version supported by both externalizer and
        // unexternalizer.  See the 'bslx' package-level documentation for more
        // information on BDEX streaming of value-semantic types and
        // containers.

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
    int maxSupportedBdexVersion() const;
        // Return the most current BDEX streaming version number supported by
        // this class.  (See the 'bslx' package-level documentation for more
        // information on BDEX streaming of value-semantic types and
        // containers.)
#endif  // BDE_OMIT_INTERNAL_DEPRECATED

    bsl::ostream& print(bsl::ostream& stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;
        // Format this object to the specified output 'stream' at the (absolute
        // value of) the optionally specified indentation 'level' and return a
        // reference to 'stream'.  If 'level' is specified, optionally specify
        // 'spacesPerLevel', the number of spaces per indentation level for
        // this and all of its nested objects.  If 'level' is negative,
        // suppress indentation of the first line.  If 'spacesPerLevel' is
        // negative, format the entire output on one line, suppressing all but
        // the initial indentation (as governed by 'level').  If 'stream' is
        // not valid on entry, this operation has no effect.

    const TYPE& value() const;
        // Return a reference providing non-modifiable access to the underlying
        // object of a (template parameter) 'TYPE'.  The behavior is undefined
        // unless this object is non-null.

    TYPE valueOr(const TYPE& value) const;
        // Return the value of the underlying object of a (template parameter)
        // 'TYPE' if this object is non-null, and the specified 'value'
        // otherwise.  Note that this method returns *by* *value*, so may be
        // inefficient in some contexts.

    #if BSLS_DEPRECATE_IS_ACTIVE(BDL, 3, 5)
    BSLS_DEPRECATE
    #endif
    const TYPE *valueOr(const TYPE *value) const;
        // !DEPRECATED!: Use 'addressOr' instead.
        //
        // Return an address providing non-modifiable access to the underlying
        // object of a (template parameter) 'TYPE' if this object is non-null,
        // and the specified 'value' otherwise.

    const TYPE *valueOrNull() const;
        // Return an address providing non-modifiable access to the underlying
        // object of a (template parameter) 'TYPE' if this object is non-null,
        // and 0 otherwise.

};

// FREE OPERATORS
template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const NullableValue<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const NullableValue<LHS_TYPE>& lhs,
                const bsl::optional<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const bsl::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' nullable objects have the
    // same value, and 'false' otherwise.  Two nullable objects have the same
    // value if both are null, or if both are non-null and the values of their
    // underlying objects compare equal.  Note that this function will fail to
    // compile if 'LHS_TYPE' and 'RHS_TYPE' are not compatible.

template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const NullableValue<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const bsl::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const NullableValue<LHS_TYPE>& lhs,
                const bsl::optional<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' nullable objects do not
    // have the same value, and 'false' otherwise.  Two nullable objects do not
    // have the same value if one is null and the other is non-null, or if both
    // are non-null and the values of their underlying objects do not compare
    // equal.  Note that this function will fail to compile if 'LHS_TYPE' and
    // 'RHS_TYPE' are not compatible.

template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const NullableValue<LHS_TYPE>& lhs,
                const RHS_TYPE&                rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator!=(const LHS_TYPE&                lhs,
                const NullableValue<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  A nullable object and a value of
    // some type do not have the same value if either the nullable object is
    // null, or its underlying value does not compare equal to the other value.
    // Note that this function will fail to compile if 'LHS_TYPE' and
    // 'RHS_TYPE' are not compatible.

template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const NullableValue<LHS_TYPE>& lhs,
                const RHS_TYPE&                rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator==(const LHS_TYPE&                lhs,
                const NullableValue<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  A nullable object and a value of some
    // type have the same value if the nullable object is non-null and its
    // underlying value compares equal to the other value.  Note that this
    // function will fail to compile if 'LHS_TYPE' and 'RHS_TYPE' are not
    // compatible.

template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const NullableValue<LHS_TYPE>& lhs,
               const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const bsl::optional<LHS_TYPE>& lhs,
               const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const NullableValue<LHS_TYPE>& lhs,
               const bsl::optional<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' nullable object is ordered before
    // the specified 'rhs' nullable object, and 'false' otherwise.  'lhs' is
    // ordered before 'rhs' if 'lhs' is null and 'rhs' is non-null or if both
    // are non-null and 'lhs.value()' is ordered before 'rhs.value()'.  Note
    // that this function will fail to compile if 'LHS_TYPE' and 'RHS_TYPE' are
    // not compatible.

template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const NullableValue<LHS_TYPE>& lhs,
               const RHS_TYPE&                rhs);
    // Return 'true' if the specified 'lhs' nullable object is ordered before
    // the specified 'rhs', and 'false' otherwise.  'lhs' is ordered before
    // 'rhs' if 'lhs' is null or 'lhs.value()' is ordered before 'rhs'.

template <class LHS_TYPE, class RHS_TYPE>
bool operator<(const LHS_TYPE&                lhs,
               const NullableValue<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' is ordered before the specified
    // 'rhs' nullable object, and 'false' otherwise.  'lhs' is ordered before
    // 'rhs' if 'rhs' is not null and 'lhs' is ordered before 'rhs.value()'.

template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const NullableValue<LHS_TYPE>& lhs,
               const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const bsl::optional<LHS_TYPE>& lhs,
               const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const NullableValue<LHS_TYPE>& lhs,
               const bsl::optional<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' nullable object is ordered after
    // the specified 'rhs' nullable object, and 'false' otherwise.  'lhs' is
    // ordered after 'rhs' if 'lhs' is non-null and 'rhs' is null or if both
    // are non-null and 'lhs.value()' is ordered after 'rhs.value()'.  Note
    // that this operator returns 'rhs < lhs' when both operands are of
    // 'NullableValue' type.  Also note that this function will fail to compile
    // if 'LHS_TYPE' and 'RHS_TYPE' are not compatible.

template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const NullableValue<LHS_TYPE>& lhs,
               const RHS_TYPE&                rhs);
    // Return 'true' if the specified 'lhs' nullable object is ordered after
    // the specified 'rhs', and 'false' otherwise.  'lhs' is ordered after
    // 'rhs' if 'lhs' is not null and 'lhs.value()' is ordered after 'rhs'.
    // Note that this operator returns 'rhs < lhs'.

template <class LHS_TYPE, class RHS_TYPE>
bool operator>(const LHS_TYPE&                lhs,
               const NullableValue<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' is ordered after the specified
    // 'rhs' nullable object, and 'false' otherwise.  'lhs' is ordered after
    // 'rhs' if 'rhs' is null or 'lhs' is ordered after 'rhs.value()'.  Note
    // that this operator returns 'rhs < lhs'.

template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const NullableValue<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const bsl::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const NullableValue<LHS_TYPE>& lhs,
                const bsl::optional<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' nullable object is ordered before
    // the specified 'rhs' nullable object or 'lhs' and 'rhs' have the same
    // value, and 'false' otherwise.  (See 'operator<' and 'operator=='.)  Note
    // that this operator returns '!(rhs < lhs)' when both operands are of
    // 'NullableValue' type.  Also note that this function will fail to compile
    // if 'LHS_TYPE' and 'RHS_TYPE' are not compatible.

template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const NullableValue<LHS_TYPE>& lhs,
                const RHS_TYPE&                rhs);
    // Return 'true' if the specified 'lhs' nullable object is ordered before
    // the specified 'rhs' or 'lhs' and 'rhs' have the same value, and 'false'
    // otherwise.  (See 'operator<' and 'operator=='.)  Note that this operator
    // returns '!(rhs < lhs)'.

template <class LHS_TYPE, class RHS_TYPE>
bool operator<=(const LHS_TYPE&                lhs,
                const NullableValue<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' is ordered before the specified
    // 'rhs' nullable object or 'lhs' and 'rhs' have the same value, and
    // 'false' otherwise.  (See 'operator<' and 'operator=='.)  Note that this
    // operator returns '!(rhs < lhs)'.

template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const NullableValue<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const bsl::optional<LHS_TYPE>& lhs,
                const NullableValue<RHS_TYPE>& rhs);
template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const NullableValue<LHS_TYPE>& lhs,
                const bsl::optional<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' nullable object is ordered after
    // the specified 'rhs' nullable object or 'lhs' and 'rhs' have the same
    // value, and 'false' otherwise.  (See 'operator>' and 'operator=='.)  Note
    // that this operator returns '!(lhs < rhs)' when both operands are of
    // 'NullableValue' type.  Also note that this function will fail to compile
    // if 'LHS_TYPE' and 'RHS_TYPE' are not compatible.

template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const NullableValue<LHS_TYPE>& lhs,
                const RHS_TYPE&                rhs);
    // Return 'true' if the specified 'lhs' nullable object is ordered after
    // the specified 'rhs' or 'lhs' and 'rhs' have the same value, and 'false'
    // otherwise.  (See 'operator>' and 'operator=='.)  Note that this operator
    // returns '!(lhs < rhs)'.

template <class LHS_TYPE, class RHS_TYPE>
bool operator>=(const LHS_TYPE&                lhs,
                const NullableValue<RHS_TYPE>& rhs);
    // Return 'true' if the specified 'lhs' is ordered after the specified
    // 'rhs' nullable object or 'lhs' and 'rhs' have the same value, and
    // 'false' otherwise.  (See 'operator>' and 'operator=='.)  Note that this
    // operator returns '!(lhs < rhs)'.

template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator==(const NullableValue<TYPE>& value,
                const NullOptType&         ) BSLS_KEYWORD_NOEXCEPT;
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator==(const NullOptType&         ,
                const NullableValue<TYPE>& value) BSLS_KEYWORD_NOEXCEPT;
    // Return 'true' if the specified 'value' is null, and 'false' otherwise.

template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator!=(const NullableValue<TYPE>& value,
                const NullOptType&         ) BSLS_KEYWORD_NOEXCEPT;
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator!=(const NullOptType&         ,
                const NullableValue<TYPE>& value) BSLS_KEYWORD_NOEXCEPT;
    // Return 'true' if the specified 'value' is not null, and 'false'
    // otherwise.

template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator<(const NullableValue<TYPE>&,
               const NullOptType&        ) BSLS_KEYWORD_NOEXCEPT;
    // Return 'false'.  Note that 'bdlb::nullOpt' never orders after a
    // 'NullableValue'.

template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator<(const NullOptType&         ,
               const NullableValue<TYPE>& value) BSLS_KEYWORD_NOEXCEPT;
    // Return 'true' if the specified 'value' is not null, and 'false'
    // otherwise.  Note that 'bdlb::nullOpt' is ordered before any
    // 'NullableValue' that is not null.

template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator>(const NullableValue<TYPE>& value,
               const NullOptType&         ) BSLS_KEYWORD_NOEXCEPT;
    // Return 'true' if the specified 'value' is not null, and 'false'
    // otherwise.

template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator>(const NullOptType&         ,
               const NullableValue<TYPE>& value) BSLS_KEYWORD_NOEXCEPT;
    // Return 'false'.  Note that 'bdlb::nullOpt' never orders after a
    // 'NullableValue'.

template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator<=(const NullableValue<TYPE>& value,
                const NullOptType&         ) BSLS_KEYWORD_NOEXCEPT;
    // Return 'true' if the specified 'value' is null, and 'false'
    // otherwise.

template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator<=(const NullOptType&         ,
                const NullableValue<TYPE>& value) BSLS_KEYWORD_NOEXCEPT;
    // Return 'true'.

template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator>=(const NullableValue<TYPE>& value,
                const NullOptType&         ) BSLS_KEYWORD_NOEXCEPT;
    // Return 'true'.

template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
bool operator>=(const NullOptType&         ,
                const NullableValue<TYPE>& value) BSLS_KEYWORD_NOEXCEPT;
    // Return 'true' if the specified 'value' is null, and 'false' otherwise.

template <class TYPE>
bsl::ostream& operator<<(bsl::ostream&              stream,
                         const NullableValue<TYPE>& object);
    // Write the value of the specified 'object' to the specified output
    // 'stream' in a single-line format, and return a reference to 'stream'.
    // If 'stream' is not valid on entry, this operation has no effect.  Note
    // that this human-readable format is not fully specified, can change
    // without notice, and is logically equivalent to:
    //..
    //  print(stream, 0, -1);
    //..

// FREE FUNCTIONS
template <class HASHALG, class TYPE>
void hashAppend(HASHALG& hashAlg, const NullableValue<TYPE>& input);
    // Pass the boolean value of whether the specified 'input' contains a value
    // to the specified 'hashAlg' hashing algorithm of (template parameter)
    // type 'HASHALG'.  If 'input' contains a value, additionally pass that
    // value to 'hashAlg'.

template <class TYPE>
typename bsl::enable_if<BloombergLP::bslma::UsesBslmaAllocator<TYPE>::value,
                        void>::type
swap(NullableValue<TYPE>& lhs, NullableValue<TYPE>& rhs);
template <class TYPE>
typename bsl::enable_if<!BloombergLP::bslma::UsesBslmaAllocator<TYPE>::value,
                        void>::type
swap(NullableValue<TYPE>& lhs, NullableValue<TYPE>& rhs);
    // Exchange the values of the specified 'rhs' and 'lhs' objects.  This
    // function provides the no-throw exception-safety guarantee if the
    // (template parameter) 'TYPE' provides that guarantee, the two objects
    // were created with the same allocator (if applicable), and the result of
    // the 'isNull' method for the two objects is the same; otherwise this
    // function provides the basic guarantee.

// ============================================================================
//                           INLINE DEFINITIONS
// ============================================================================

                      // -------------------------
                      // class NullableValue<TYPE>
                      // -------------------------

// CREATORS
template <class TYPE>
inline
NullableValue<TYPE>::NullableValue() BSLS_KEYWORD_NOEXCEPT
{
}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(
                         const allocator_type& allocator) BSLS_KEYWORD_NOEXCEPT
: bsl::optional<TYPE>(bsl::allocator_arg, allocator)
{
}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(const NullableValue& original)
: bsl::optional<TYPE>(static_cast<const bsl::optional<TYPE>&>(original))
{
}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(const NullableValue&  original,
                                   const allocator_type& allocator)
: bsl::optional<TYPE>(bsl::allocator_arg,
                      allocator,
                      static_cast<const bsl::optional<TYPE>&>(original))
{
}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(bslmf::MovableRef<NullableValue> original)
: bsl::optional<TYPE>(MoveUtil::move(
      static_cast<bsl::optional<TYPE>&>(MoveUtil::access(original))))
{
}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(bslmf::MovableRef<NullableValue> original,
                                   const allocator_type&            allocator)
: bsl::optional<TYPE>(bsl::allocator_arg,
                      allocator,
                      MoveUtil::move(static_cast<bsl::optional<TYPE>&>(
                          MoveUtil::access(original))))
{
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
    BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value,
    typename bsl::enable_if<
        bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value &&
            !bsl::is_convertible<BDE_OTHER_TYPE, allocator_type>::value,
        EnableType>::type)
: bsl::optional<TYPE>(BSLS_COMPILERFEATURES_FORWARD(BDE_OTHER_TYPE, value))
{
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
    BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value,
    const allocator_type&                             allocator,
    typename bsl::enable_if<bsl::is_convertible<BDE_OTHER_TYPE, TYPE>::value,
                            EnableType>::type)
: bsl::optional<TYPE>(bsl::allocator_arg,
                      allocator,
                      BSLS_COMPILERFEATURES_FORWARD(BDE_OTHER_TYPE, value))
{
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
    const NullableValue<BDE_OTHER_TYPE>& original)
: bsl::optional<TYPE>(
      static_cast<const bsl::optional<BDE_OTHER_TYPE>&>(original))
{
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>::NullableValue(
    const NullableValue<BDE_OTHER_TYPE>& original,
    const allocator_type&                allocator)
: bsl::optional<TYPE>(
      bsl::allocator_arg,
      allocator,
      static_cast<const bsl::optional<BDE_OTHER_TYPE>&>(original))
{
}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(const NullOptType&) BSLS_KEYWORD_NOEXCEPT
: bsl::optional<TYPE>()
{
}

template <class TYPE>
inline
NullableValue<TYPE>::NullableValue(
                         const NullOptType&,
                         const allocator_type& allocator) BSLS_KEYWORD_NOEXCEPT
: bsl::optional<TYPE>(bsl::allocator_arg, allocator)
{
}

// MANIPULATORS
template <class TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(const NullableValue& rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes in behaviour, we implement the
    // assignment in 'NullableValue' directly.

    if (rhs.has_value()) {
        if (this->has_value()) {
            this->value() = rhs.value();
        }
        else {
            this->emplace(rhs.value());
        }
    }
    else {
        this->reset();
    }
    return *this;
}

template <class TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(
                                          bslmf::MovableRef<NullableValue> rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes in behaviour, we implement the
    // assignment in 'NullableValue' directly.

    NullableValue& lvalue = rhs;

    if (lvalue.has_value()) {
        if (this->has_value()) {
            this->value() = MoveUtil::move(lvalue.value());
        }
        else {
            this->emplace(MoveUtil::move(lvalue.value()));
        }
    }
    else {
        this->reset();
    }
    return *this;
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
NullableValue<TYPE>& NullableValue<TYPE>::operator=(
                                      const NullableValue<BDE_OTHER_TYPE>& rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes in behaviour, we implement the
    // assignment in 'NullableValue' directly.

    if (rhs.has_value()) {
        if (this->has_value()) {
            this->value() = rhs.value();
        }
        else {
            this->emplace(rhs.value());
        }
    }
    else {
        this->reset();
    }
    return *this;
}

template <class TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(const TYPE& rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes in behaviour, we implement the
    // assignment in 'NullableValue' directly.

    if (this->has_value()) {
        this->value() = rhs;
    }
    else {
        this->emplace(rhs);
    }
    return *this;
}

template <class TYPE>
template <class STREAM>
STREAM& NullableValue<TYPE>::bdexStreamIn(STREAM& stream, int version)
{
    using bslx::InStreamFunctions::bdexStreamIn;

    char isNull = 0;  // Redundant initialization to suppress -Werror.

    stream.getInt8(isNull);

    if (stream) {
        if (!isNull) {
            bdexStreamIn(stream, this->emplace(), version);
        }
        else {
            this->reset();
        }
    }

    return stream;
}

template <class TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(
                                                   bslmf::MovableRef<TYPE> rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes in behaviour, we implement the
    // assignment in 'NullableValue' directly.

    if (this->has_value()) {
        this->value() = MoveUtil::move(rhs);
    }
    else {
        this->emplace(MoveUtil::move(rhs));
    }
    return *this;
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(const BDE_OTHER_TYPE& rhs)
{
    // Constraints on 'bsl::optional' assignment operator may affect the
    // assignment.  In order to avoid changes in behaviour, we implement the
    // assignment in 'NullableValue' directly.

    if (this->has_value()) {
        this->value() = rhs;
    }
    else {
        this->emplace(rhs);
    }
    return *this;
}

template <class TYPE>
inline
NullableValue<TYPE>& NullableValue<TYPE>::operator=(
                                      const NullOptType&) BSLS_KEYWORD_NOEXCEPT
{
    this->reset();

    return *this;
}

template <class TYPE>
template <class BDE_OTHER_TYPE>
inline
TYPE& NullableValue<TYPE>::makeValue(
                       BSLS_COMPILERFEATURES_FORWARD_REF(BDE_OTHER_TYPE) value)
{
    return this->emplace(BSLS_COMPILERFEATURES_FORWARD(BDE_OTHER_TYPE, value));
}

template <class TYPE>
inline
TYPE& NullableValue<TYPE>::makeValue()
{
    return this->emplace();
}

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
template <class TYPE>
template <class... ARGS>
inline
TYPE& NullableValue<TYPE>::makeValueInplace(ARGS&&... args)
{
    return this->emplace(BSLS_COMPILERFEATURES_FORWARD(ARGS, args)...);
}
#endif

template <class TYPE>
inline
TYPE& NullableValue<TYPE>::value()
{
    BSLS_REVIEW_OPT(this->has_value());

    return this->dereferenceRaw();
}
// ACCESSORS
template <class TYPE>
inline
const TYPE *NullableValue<TYPE>::addressOr(const TYPE *address) const
{
    return this->has_value() ? this-> operator->() : address;
}

template <class TYPE>
template <class STREAM>
STREAM& NullableValue<TYPE>::bdexStreamOut(STREAM& stream, int version) const
{
    using bslx::OutStreamFunctions::bdexStreamOut;

    const bool isNull = !this->has_value();

    stream.putInt8(isNull ? 1 : 0);

    if (!isNull) {
        bdexStreamOut(stream, this->value(), version);
    }

    return stream;
}

template <class TYPE>
inline
bool NullableValue<TYPE>::isNull() const BSLS_KEYWORD_NOEXCEPT
{
    return !this->has_value();
}

template <class TYPE>
inline
int NullableValue<TYPE>::maxSupportedBdexVersion(int versionSelector) const
{
    using bslx::VersionFunctions::maxSupportedBdexVersion;

    // We need to call the 'bslx::VersionFunctions' helper function, because we
    // cannot guarantee that 'TYPE' implements 'maxSupportedBdexVersion' as a
    // class method.

    return maxSupportedBdexVersion(reinterpret_cast<TYPE *>(0),
                                   versionSelector);
}

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
template <class TYPE>
inline
int NullableValue<TYPE>::maxSupportedBdexVersion() const
{
    using bslx::VersionFunctions::maxSupportedBdexVersion;

    // We need to call the 'bslx::VersionFunctions' helper function, because we
    // cannot guarantee that 'TYPE' implements 'maxSupportedBdexVersion' as a
    // class method.

    return maxSupportedBdexVersion(reinterpret_cast<TYPE *>(0));
}
#endif  // BDE_OMIT_INTERNAL_DEPRECATED

template <class TYPE>
bsl::ostream& NullableValue<TYPE>::print(bsl::ostream& stream,
                                         int           level,
                                         int           spacesPerLevel) const
{
    if (!this->has_value()) {
        return bdlb::PrintMethods::print(stream,
                                         "NULL",
                                         level,
                                         spacesPerLevel);             // RETURN
    }

    return bdlb::PrintMethods::print(
        stream, this->value(), level, spacesPerLevel);
}

template <class TYPE>
inline
const TYPE& NullableValue<TYPE>::value() const
{
    BSLS_REVIEW_OPT(this->has_value());

    return this->dereferenceRaw();
}

template <class TYPE>
inline
TYPE NullableValue<TYPE>::valueOr(const TYPE& value) const
{
    return this->value_or(value);
}

template <class TYPE>
inline
const TYPE *NullableValue<TYPE>::valueOr(const TYPE *value) const
{
    return this->has_value() ? this-> operator->() : value;
}

template <class TYPE>
inline
const TYPE *NullableValue<TYPE>::valueOrNull() const
{
    return this->has_value() ? this-> operator->() : 0;
}

}  // close package namespace

// FREE OPERATORS
template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const NullableValue<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) ==
           static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const bsl::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
{
    return lhs == static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const NullableValue<LHS_TYPE>& lhs,
                      const bsl::optional<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) == rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const NullableValue<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) !=
           static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const bsl::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
{
    return lhs != static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const NullableValue<LHS_TYPE>& lhs,
                      const bsl::optional<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) != rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const NullableValue<LHS_TYPE>& lhs, const RHS_TYPE& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) == rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator==(const LHS_TYPE& lhs, const NullableValue<RHS_TYPE>& rhs)
{
    return lhs == static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const NullableValue<LHS_TYPE>& lhs, const RHS_TYPE& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) != rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator!=(const LHS_TYPE& lhs, const NullableValue<RHS_TYPE>& rhs)
{
    return lhs != static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const NullableValue<LHS_TYPE>& lhs,
                     const NullableValue<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) <
           static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const bsl::optional<LHS_TYPE>& lhs,
                     const NullableValue<RHS_TYPE>& rhs)
{
    return lhs < static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const NullableValue<LHS_TYPE>& lhs,
                     const bsl::optional<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) < rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const NullableValue<LHS_TYPE>& lhs, const RHS_TYPE& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) < rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<(const LHS_TYPE& lhs, const NullableValue<RHS_TYPE>& rhs)
{
    return lhs < static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const NullableValue<LHS_TYPE>& lhs,
                     const NullableValue<RHS_TYPE>& rhs)
{
    return rhs < lhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const bsl::optional<LHS_TYPE>& lhs,
                     const NullableValue<RHS_TYPE>& rhs)
{
    return lhs > static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const NullableValue<LHS_TYPE>& lhs,
                     const bsl::optional<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) > rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const NullableValue<LHS_TYPE>& lhs,
                     const RHS_TYPE&                rhs)
{
    return rhs < lhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>(const LHS_TYPE&                lhs,
                     const NullableValue<RHS_TYPE>& rhs)
{
    return rhs < lhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const NullableValue<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
{
    return !(rhs < lhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const bsl::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
{
    return lhs <= static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const NullableValue<LHS_TYPE>& lhs,
                      const bsl::optional<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) <= rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const NullableValue<LHS_TYPE>& lhs,
                      const RHS_TYPE&                rhs)
{
    return !(rhs < lhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator<=(const LHS_TYPE&                lhs,
                      const NullableValue<RHS_TYPE>& rhs)
{
    return !(rhs < lhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const NullableValue<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
{
    return !(lhs < rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const bsl::optional<LHS_TYPE>& lhs,
                      const NullableValue<RHS_TYPE>& rhs)
{
    return lhs >= static_cast<const bsl::optional<RHS_TYPE>&>(rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const NullableValue<LHS_TYPE>& lhs,
                      const bsl::optional<RHS_TYPE>& rhs)
{
    return static_cast<const bsl::optional<LHS_TYPE>&>(lhs) >= rhs;
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const NullableValue<LHS_TYPE>& lhs,
                      const RHS_TYPE&                rhs)
{
    return !(lhs < rhs);
}

template <class LHS_TYPE, class RHS_TYPE>
inline
bool bdlb::operator>=(const LHS_TYPE&                lhs,
                      const NullableValue<RHS_TYPE>& rhs)
{
    return !(lhs < rhs);
}

template <class TYPE>
inline
BSLS_KEYWORD_CONSTEXPR bool bdlb::operator==(
                             const NullableValue<TYPE>& value,
                             const NullOptType&) BSLS_KEYWORD_NOEXCEPT
{
    return value.isNull();
}

template <class TYPE>
inline
BSLS_KEYWORD_CONSTEXPR bool bdlb::operator==(
                        const NullOptType&,
                        const NullableValue<TYPE>& value) BSLS_KEYWORD_NOEXCEPT
{
    return value.isNull();
}

template <class TYPE>
inline
BSLS_KEYWORD_CONSTEXPR bool bdlb::operator!=(
                             const NullableValue<TYPE>& value,
                             const NullOptType&) BSLS_KEYWORD_NOEXCEPT
{
    return !value.isNull();
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator!=(const NullOptType&         ,
                      const NullableValue<TYPE>& value) BSLS_KEYWORD_NOEXCEPT
{
    return !value.isNull();
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator<(const NullableValue<TYPE>&,
                     const NullOptType&        ) BSLS_KEYWORD_NOEXCEPT
{
    return false;
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator<(const NullOptType&         ,
                     const NullableValue<TYPE>& value) BSLS_KEYWORD_NOEXCEPT
{
    return !value.isNull();
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator>(const NullableValue<TYPE>& value,
                     const NullOptType&         ) BSLS_KEYWORD_NOEXCEPT
{
    return !value.isNull();
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator>(const NullOptType&         ,
                     const NullableValue<TYPE>& ) BSLS_KEYWORD_NOEXCEPT
{
    return false;
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator<=(const NullableValue<TYPE>& value,
                      const NullOptType&         ) BSLS_KEYWORD_NOEXCEPT
{
    return value.isNull();
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator<=(const NullOptType&         ,
                      const NullableValue<TYPE>& ) BSLS_KEYWORD_NOEXCEPT
{
    return true;
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator>=(const NullableValue<TYPE>& ,
                      const NullOptType&         ) BSLS_KEYWORD_NOEXCEPT
{
    return true;
}

template <class TYPE>
inline BSLS_KEYWORD_CONSTEXPR
bool bdlb::operator>=(const NullOptType&  ,
                      const NullableValue<TYPE>& value) BSLS_KEYWORD_NOEXCEPT
{
    return value.isNull();
}

template <class TYPE>
inline
bsl::ostream& bdlb::operator<<(bsl::ostream&              stream,
                               const NullableValue<TYPE>& object)
{
    return object.print(stream, 0, -1);
}

// FREE FUNCTIONS
template <class HASHALG, class TYPE>
void bdlb::hashAppend(HASHALG& hashAlg, const NullableValue<TYPE>& input)
{
    if (!input.isNull()) {
        hashAppend(hashAlg, true);
        hashAppend(hashAlg, input.value());
    }
    else {
        hashAppend(hashAlg, false);
    }
}

template <class TYPE>
inline
typename bsl::enable_if<BloombergLP::bslma::UsesBslmaAllocator<TYPE>::value,
                        void>::type
bdlb::swap(NullableValue<TYPE>& lhs, NullableValue<TYPE>& rhs)
{
    swap(static_cast<bsl::optional<TYPE>&>(lhs),
         static_cast<bsl::optional<TYPE>&>(rhs));
}

template <class TYPE>
inline
typename bsl::enable_if<!BloombergLP::bslma::UsesBslmaAllocator<TYPE>::value,
                        void>::type
bdlb::swap(NullableValue<TYPE>& lhs, NullableValue<TYPE>& rhs)
{
    lhs.swap(rhs);
}

}  // close enterprise namespace

#endif // End C++11 code

#endif  // INCLUDED_BDLB_NULLABLEVALUE

// ----------------------------------------------------------------------------
// Copyright 2016 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
